源码赏析

Alamofire是Swift编写的网络库,如果你的项目使用Swift编写的,则可以用它来替代AFNetworking。Alamofire和AFNetworking都是由Mattt Thompson发起的。Alamofire相比AFNetworking而言,利用了Swift语言的特性,来使得代码更加优雅,并具有一些强大的特性。

下面我们从源码中来一一赏析这些。

结构设计

基于Swift语言的简洁,Alamofire的文件并不多,分成了两个目录Core和Feature,非常的清晰。

Core目录中包含了核心的2个类和3个枚举和2个结构体。

2个核心类:

  • Manager 提供对外接口,处理NSURLSession的delegate

  • Request 对请求的处理

3个枚举:

  • Method HTTP Method

  • ParameterEncoding 参数编码方式(JSON、PropertyList)

  • Result 定义了请求成功或失败的数据结构

2个结构体:

  • Response 封装了响应需要包含的数据结构

  • Error 错误码定义;创建NSError对象;

Features目录中则是对这些核心数据结构的扩展:

  • Upload 处理上传相关操作,是Manager类的扩展
  • Download 处理下载相关操作,是Manager类的扩展
  • Stream 处理输入输出流,是Manager类的扩展
  • Validation 对Response数据进行校验,是Request类的扩展
  • ResponseSerialization Response的序列化处理,是Request类的扩展
  • MultipartFormData 自己是一个类,被Request的Upload引用,处
  • MultipartFormData
  • ServerTrustPolicyManager 自己是一个类,被NSURLSession引用,处理安全策略

可以看到并不是每个文件都是一个类,Upload、Download、Stream这些操作都以extension的形式放到单独的文件中,做到了不仅由Manager类来统一管理,也按职责进行了拆分。

同样Validation和ResponseSerialization也以Request类的扩展的形式存在,并支持Request类的链式调用。

类图如下:

职责划分明确,有着很好的内聚性。

枚举

凡是需要类型划分的都用枚举来实现,例如:

public enum ParameterEncoding {
    case URL
    case URLEncodedInURL
    case JSON
    case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions)
    case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSMutableURLRequest, NSError?))

基于Swift的特性,在枚举中可以定义方法来实现“对外统一接口,内部根据自己当前的枚举值有着不同的实现”,这是Swift中很常见的写法。

public func encode(...) {
    ...
    switch self {
    case .JSON:
        ...
    case .PropertyList(let format, let options):
        ...
    }
    ...
}

再举个Result的例子:

public enum Result<Value, Error: ErrorType> {
    case Success(Value)
    case Failure(Error)

    /// Returns `true` if the result is a success, `false` otherwise.
    public var isSuccess: Bool {
        switch self {
        case .Success:
            return true
        case .Failure:
            return false
        }
    }
    ...    
}

结构体

对于那些没有复杂业务逻辑,主要用来保存数据的结构,用结构体来实现,例如Response:

public struct Response<Value, Error: ErrorType> {
    /// The URL request sent to the server.
    public let request: NSURLRequest?

    /// The server's response to the URL request.
    public let response: NSHTTPURLResponse?

    /// The data returned by the server.
    public let data: NSData?

    /// The result of response serialization.
    public let result: Result<Value, Error>

    /**
        Initializes the `Response` instance with the specified URL request, URL response, server data and response
        serialization result.

        - parameter request:  The URL request sent to the server.
        - parameter response: The server's response to the URL request.
        - parameter data:     The data returned by the server.
        - parameter result:   The result of response serialization.

        - returns: the new `Response` instance.
    */
    public init(request: NSURLRequest?, response: NSHTTPURLResponse?, data: NSData?, result: Result<Value, Error>) {
        self.request = request
        self.response = response
        self.data = data
        self.result = result
    }
}

泛型

Alamofire中有着大量对泛型的使用

public func validate<S: SequenceType where S.Generator.Element == Int>(statusCode acceptableStatusCode: S) -> Self {
public enum Result<Value, Error: ErrorType> {

命名空间

Alamofire是一个Framework,享有独立的命名空间,因此所有的类、结构体、枚举等的命名都不需要加前缀。

链式调用

对于Request类,其中的很多方法都使用了返回-> Self的方式。

请求

public func request(
    method: Method,
    _ URLString: URLStringConvertible,
    parameters: [String: AnyObject]? = nil,
    encoding: ParameterEncoding = .URL,
    headers: [String: String]? = nil)
    -> Request

鉴权

public func authenticate(
    user user: String,
    password: String,
    persistence: NSURLCredentialPersistence = .ForSession)
    -> Self

进度

public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self

响应

public func response(
    queue queue: dispatch_queue_t? = nil,
    completionHandler: (NSURLRequest?, NSHTTPURLResponse?, NSData?, NSError?) -> Void)
    -> Self

验证

public func validate(validation: Validation) -> Self

调用示例:

Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
         .validate(statusCode: 200..<300)
         .validate(contentType: ["application/json"])
         .response { response in
             print(response)
         }
Alamofire.request(...)

这一步首先会创建一个Request实例,这个实例中启动了一个NSURLSessionTask,这时,一个请求已经被发出。

Request类中有个内部类TaskDelegate用来处理与这个Task相对应的NSURLSession回调,TaskDelegate维护了一个Serial的 NSOperationQueue(maxConcurrentOperationCount = 1),TaskDelegate会随着Request一起创建,这个queue同时也被加载到内存中。

接下来的链式调用创建的操作闭包都会追加到这个queue中。

.validate(statusCode: 200..<300)

把一个用来验证StatusCode的操作闭包追加到queue,如果验证失败,会在TaskDelegate中保存一个NSError

.validate(contentType: ["application/json"])

把一个用来验证contentType的操作闭包追加到queue,如果验证失败,会在TaskDelegate中保存一个NSError

.response { response in
    print(response)
}

把一个操作闭包(内部创建的闭包,不是response之后跟随的这个闭包)添加到queue,这个闭包中做了如下处理:

  1. 检查TaskDelegate中是否保存了NSError,如果有,则直接把NSError放到response中,并且回调给调用方

  2. 选择一种方式来序列化response

  3. 把序列化的结果放到response参数中,并在主线程调用response之后跟随的这个闭包。

至此链式调用完成。

有个小细节是,request之后追加到queue中的这些操作闭包都是对response进行处理的。所以在queue创建的时候要把queue挂起。

operationQueue.suspended = true

在NSURLSession回调结束(URLSession(_:task:didCompleteWithError:))后恢复queue。

queue.suspended = false

一句话总结:

Alamofire使用了-> Self和顺序队列来实现链式调用。

处理多请求并发

Alamofire的最外层是一个Manager,通过这个Manager来为每一个请求创建Request实例(Request实例背后是NSURLSessionTask)。

Manager中有个实例变量queue,是一个Serial Dispatch Queue。

public func request(URLRequest: URLRequestConvertible) -> Request {
    var dataTask: NSURLSessionDataTask!

    dispatch_sync(queue) {
        dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
    }

    let request = Request(session: session, task: dataTask)
    delegate[request.delegate.task] = request.delegate

    if startRequestsImmediately {
        request.resume()
    }

    return request
}

当同时有多个请求被创建时,每个Task的创建操作被同步追加到queue中,并等待它创建完成后,才执行后面的代码。

dispatch_sync(queue) {
    dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
}

就是说,每个请求是按照顺序一个接一个创建的。

Manager有个内部类SessionDelegate,用来处理NSURLSession的回调方法。Manager在创建时,把Manager引用的NSURLSession对象的delegate赋值为这个SessionDelegate对象。

前面说到,每个请求创建的同时,一个对应的TaskDelegate实例也会创建,这个实例中保存了请求对应的NSURLSessionTask, 请求被创建后,会把相应的TaskDelegate以task为key保存在SessionDelegate中的一个实例变量字典中。 (这里有点绕,如果难以理解可以去参考下源码。)

当发出多次请求后,SessionDelegate中会保存多个TaskDelegate,每个TaskDelegate中都有一个NSURLSessionTask。

每次NSURLSession的回调回来以后,Manager会根据回调参数中的task(例如URLSession(_:task:didCompleteWithError:)中的task参数),以这个task为key,取出对应的TaskDelegate,并把回调分发给它。

(以task为key通过Swift语法的中下标subscript来实现。)

这就是Alamofire的并发请求处理方式。

总结:

  1. 顺序创建Task,以Task为key保存TaskDelegate。
  2. 由Manager统一并发地处理NSURLSession的回调。
  3. 在NSURLSession的回调中根据task把回调分发给对应的TaskDelegate。

results matching ""

    No results matching ""